package com.sxhuayuan.parking.plugin.payment;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import javax.annotation.Resource;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sxhuayuan.parking.compenent.domain.Setting;
import com.sxhuayuan.parking.compenent.web.RespMessage;
import com.sxhuayuan.parking.config.OrderType;
import com.sxhuayuan.parking.config.Platform;
import com.sxhuayuan.parking.entity.Member;
import com.sxhuayuan.parking.entity.MonthCardOrder;
import com.sxhuayuan.parking.entity.OemConfig;
import com.sxhuayuan.parking.entity.Order;
import com.sxhuayuan.parking.entity.OrderRefund;
import com.sxhuayuan.parking.entity.ParkingLot;
import com.sxhuayuan.parking.exception.MyException;
import com.sxhuayuan.parking.service.MemberService;
import com.sxhuayuan.parking.service.MonthCardOrderService;
import com.sxhuayuan.parking.service.OrderRefundService;
import com.sxhuayuan.parking.service.OrderService;
import com.sxhuayuan.parking.service.ParkingLotService;
import com.sxhuayuan.parking.utils.DateUtils;
import com.sxhuayuan.parking.utils.Md5Utils;
import com.sxhuayuan.parking.utils.SettingUtils;
import com.sxhuayuan.parking.utils.SignatureUtils;
import com.sxhuayuan.parking.utils.StringArray;

import weixin.popular.api.PayMchAPI;
import weixin.popular.bean.paymch.Authcodetoopenid;
import weixin.popular.bean.paymch.AuthcodetoopenidResult;

@Component(value = WxPayPlugin.ID)
public class WxPayPlugin {

	public static final String ID = "wxPayPlugin";
	public static final String SUCCESS = "SUCCESS";
	public static final String FAIL = "FAIL";

	Logger log = LoggerFactory.getLogger(getClass());

	@Resource(name = "parkingLotServiceImpl")
	ParkingLotService lotService;
	@Resource(name = "orderServiceImpl")
	OrderService orderService;
	@Resource(name = "memberServiceImpl")
	MemberService memberService;
	@Resource(name = "monthCardOrderServiceImpl")
	MonthCardOrderService monthCardOrderService;
	@Autowired
	OrderRefundService refundService;

	/**
	 * 停车统一下单
	 * 
	 * @param order
	 * @return
	 * @throws Exception
	 */
	public ResultPayJsonData unified(Order order) throws Exception {
		ParkingLot lot = order.getParkingLot();
		Member member = memberService.findOne(order.getMemberId());
		BigDecimal money = order.getMoney();
		if (lot.getIsDebug() && SettingUtils.get().isDebugCarPlate(order.getCarPlate())) {
			log.debug("==========测试车牌,统一支付0.01===========");
			money = new BigDecimal(0.01f);
		}
		return unified(lot, member, money, order.getPlatform(), order.getSn(),
				String.format("%s, %s到%s", order.getCarPlate(), DateUtils.format(order.getEntertime(), "MM-dd HH:mm"), DateUtils.format(order.getEndtime(), "MM-dd HH:mm")));
	}

	/** 月卡统一下单 */
	public ResultPayJsonData unified(MonthCardOrder order) throws Exception {
		ParkingLot lot = order.getParkingLot();
		return unified(lot, order.getMember(), order.getMoney(), order.getPlatform(), order.getSn(), "月卡充值, " + order.getCarPlate());
	}

	/** 统一下单 */

	public ResultPayJsonData unified(ParkingLot lot, Member member, BigDecimal money, Platform platform, String orderNo, String goodsName) throws Exception {

		Assert.notNull(member, "下单时member为空");

		BigDecimal fee = SettingUtils.get().setScale(money);

		if (fee.compareTo(BigDecimal.ZERO) <= 0) {
			throw new MyException("金额不能小于0");
		}

		/** 公共请求map */
		Map<String, Object> requestMap = new HashMap<>();
		// 报文格式
		requestMap.put("format", "json");
		// 时间搓
		requestMap.put("timestamp", System.currentTimeMillis() + "");
		// 随机字符串
		requestMap.put("noncestr", RandomStringUtils.randomAlphanumeric(12));
		// 子商户号
		requestMap.put("mchid", lot.getMchid());
		// 命令 order
		requestMap.put("order", "unified");

		String url = null;
		/** 判断当前车场采用支付通道 */
		if (lot.getPayMchType().equals(ParkingLot.PAY_MCH_TYPE_OFFICIAL)) {
			/** 官方通道 */
			url = SettingUtils.get().getOfficialPass();
		} else {
			/** 银行通道 */
			url = SettingUtils.get().getBankPass();
		}
		/** 实际请求内容 */
		JSONObject content = new JSONObject();
		// 交易类型
		content.put("trade_type", "jssdk");
		// 支付平台
		content.put("platform", platform.name());
		// 第三方订单号
		content.put("out_trade_no", orderNo);
		// openId
		content.put("openid", member.getOpenid());
		// 交易金额
		content.put("money", String.format("%.2f", fee));
		content.put("goods_name", goodsName);
		// 回调地址
		content.put("notify_url", getNotifyUrl(OrderType.park, orderNo));
		if (lot.getPayMchType().equals(ParkingLot.PAY_MCH_TYPE_OFFICIAL))
			content.put("device", lot.getSn());
		else {
			content.put("attach", lot.getSn());
		}
		// 报文内容
		requestMap.put("content", content.toJSONString());

		String sign = SignatureUtils.signMd5(requestMap, "key", lot.getMchkey());
		// 签名
		requestMap.put("sign", sign);
		String uuid = UUID.randomUUID().toString();
		log.debug("[{}]request = {}", uuid, requestMap.get("content"));
		String resp = null;
		CloseableHttpClient httpClient = HttpClients.createDefault();
		CloseableHttpResponse httpResponse = null;
		try {
			HttpPost httpPost = new HttpPost(url);
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
			if (requestMap != null) {
				for (Entry<String, Object> entry : requestMap.entrySet()) {
					String name = entry.getKey();
					// ConvertUtils.convert()
					String value = entry.getValue().toString();
					if (StringUtils.isNotEmpty(name)) {
						nameValuePairs.add(new BasicNameValuePair(name, value));
					}
				}
			}
			httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "utf-8"));
			httpResponse = httpClient.execute(httpPost);
			HttpEntity httpEntity = httpResponse.getEntity();
			resp = EntityUtils.toString(httpEntity, "utf-8");
			EntityUtils.consume(httpEntity);
			log.debug("[{}]resp={}", uuid, resp);
			if (resp != null) {
				String[] resps = resp.split("&");
				Map<String, String> respMap = new HashMap<>();
				for (String s : resps) {
					String[] arr = s.split("=", 2);
					respMap.put(arr[0], arr[1]);
				}
				if (respMap.containsKey("resp_code") && isSuccess(respMap.get("resp_code"))) {
					String content2 = respMap.get("content");
					ResultPayJsonData rs = JSON.parseObject(content2, ResultPayJsonData.class);
					return rs;
					// if (StringUtils.equalsIgnoreCase(SignatureUtils.generateSign(respMap,
					// secret), respMap.get("sign"))) {
					// } else {
					// log.error("签名验证未通过");
					// response.setError("签名验证未通过");
					// }
				} else {
					throw new MyException(RespMessage.Status.ERROR, respMap.get("resp_info"));
				}
			} else {
				throw new MyException(RespMessage.Status.ERROR, "支付服务不可用");
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(httpResponse);
			IOUtils.closeQuietly(httpClient);
		}
		return null;
	}

	/**
	 * 支付授权码获取openid
	 * 
	 * @param authcode
	 */
	public Authcode2openidResp authcode2openid(ParkingLot lot, String authcode) {
		OemConfig config = lot.getAgent().getOemConfig();
		Authcodetoopenid authcodetoopenid = new Authcodetoopenid();
		authcodetoopenid.setAppid(config.getWxAppid());
		authcodetoopenid.setAuth_code(authcode);
		authcodetoopenid.setMch_id(config.getWxMchid());
		authcodetoopenid.setNonce_str(RandomStringUtils.randomAlphabetic(8));
		authcodetoopenid.setSub_appid(config.getWxSubAppid());
		authcodetoopenid.setSub_mch_id(config.getWxSubMchid());
		AuthcodetoopenidResult result = PayMchAPI.toolsAuthcodetoopenid(authcodetoopenid, config.getWxMchKey());
		Authcode2openidResp rs = new Authcode2openidResp();
		rs.setOpenid(result.getOpenid());
		rs.setSubOpenid(result.getSub_openid());
		return rs;
	}

	private boolean isSuccess(String respCode) {
		return StringUtils.equals("0000", respCode);
	}

	/** Content内容处理JSON */
	public String jsonContent(Map<String, Object> mapContent) {
		JSONObject jsonpObject = new JSONObject(mapContent);
		String contentJson = jsonpObject.toJSONString();
		return contentJson;
	}

	/** 生成sign */
	public Map<String, Object> makeSign(Map<String, Object> publicData) {
		String[] arr = { "mchid=" + publicData.get("mchid"), "order=" + publicData.get("orders"), "format=" + publicData.get("format"), "timestamp=" + publicData.get("timestamp"),
				"noncestr=" + publicData.get("noncestr"), "content=" + publicData.get("content") };
		String strA = StringArray.strArray(arr);
		String stringSignTemp = strA + "key=" + publicData.get("key");
		System.out.println(stringSignTemp);
		String sign = Md5Utils.md5(stringSignTemp);
		publicData.put("sign", sign);

		return publicData;
	}

	protected static String getNotifyUrl(OrderType type, String sn) {
		Setting setting = SettingUtils.get();
		return setting.getSiteUrl() + "/common/pay/notify/type/" + type;
	}

	private String post(String url, Map<String, Object> requestMap, String key) {
		String sign = SignatureUtils.signMd5(requestMap, "key", key);
		// 签名
		requestMap.put("sign", sign);
		String uuid = UUID.randomUUID().toString();
		log.debug("[{}]request = {}", uuid, requestMap.get("content"));
		String resp = null;
		CloseableHttpClient httpClient = HttpClients.createDefault();
		CloseableHttpResponse httpResponse = null;
		try {
			HttpPost httpPost = new HttpPost(url);
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
			if (requestMap != null) {
				for (Entry<String, Object> entry : requestMap.entrySet()) {
					String name = entry.getKey();
					// ConvertUtils.convert()
					Object value = entry.getValue();
					if (StringUtils.isNotEmpty(name)) {
						nameValuePairs.add(new BasicNameValuePair(name, String.valueOf(value)));
					}
				}
			}
			httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "utf-8"));
			httpResponse = httpClient.execute(httpPost);
			HttpEntity httpEntity = httpResponse.getEntity();
			resp = EntityUtils.toString(httpEntity, "utf-8");
			EntityUtils.consume(httpEntity);
			log.debug("[{}]resp={}", uuid, resp);
			if (resp != null) {
				String[] resps = resp.split("&");
				Map<String, Object> respMap = new HashMap<>();
				for (String s : resps) {
					String[] arr = s.split("=", 2);
					respMap.put(arr[0], arr[1]);
				}
				if (respMap.containsKey("resp_code") && isSuccess(respMap.get("resp_code").toString())) {
					String content2 = respMap.get("content").toString();
					if (!SignatureUtils.checkHuabeiPaySign(respMap, key)) {
						log.error("签名验证未通过");
						throw new MyException(RespMessage.Status.ERROR, "签名验证未通过");
					}
					return content2;
				} else {
					throw new MyException(RespMessage.Status.ERROR, respMap.get("resp_info").toString());
				}
			} else {
				throw new MyException(RespMessage.Status.ERROR, "支付服务不可用");
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(httpResponse);
			IOUtils.closeQuietly(httpClient);
		}
		return null;
	}

	// 退款
	public OrderRefund refund(Order order, BigDecimal refundMoney, String note) {
		ParkingLot lot = order.getParkingLot();
		/** 公共请求map */
		Map<String, Object> requestMap = new HashMap<>();
		// 报文格式
		requestMap.put("format", "json");
		// 时间搓
		requestMap.put("timestamp", System.currentTimeMillis() + "");
		// 随机字符串
		requestMap.put("noncestr", RandomStringUtils.randomAlphanumeric(12));
		// 子商户号
		requestMap.put("mchid", lot.getMchid());
		// 命令 order
		requestMap.put("order", "refund");

		/** 实际请求内容 */
		JSONObject content = new JSONObject();
		content.put("trade_no", order.getTradeNo());
		content.put("refund_money", refundMoney);
		OrderRefund refund = refundService.createRefund(order, refundMoney, note);
		content.put("out_refund_no", refund.getRefundNo());
		// 报文内容
		requestMap.put("content", content.toJSONString());

		String url = null;
		/** 判断当前车场采用支付通道 */
		if (lot.getPayMchType().equals(ParkingLot.PAY_MCH_TYPE_OFFICIAL)) {
			/** 官方通道 */
			url = SettingUtils.get().getOfficialPass();
		} else {
			/** 银行通道 */
			url = SettingUtils.get().getBankPass();
		}
		try {
			String respJson = post(url, requestMap, lot.getMchkey());
			RefundResult result = JSON.parseObject(respJson, RefundResult.class);
			if (result == null) {
				result = queryRefund(url, lot.getMchid(), lot.getMchkey(), refund.getRefundNo());
			}
			if (result == null) {
				refundService.refundSuccess(refund, "网络中断，退款结果未知，暂时按成功处理");
			}
			if (result != null && result.getIsSuccess()) {
				refundService.refundSuccess(refund, "");
			}
			// 其他情况都属于退款失败，不用做更新
			return refund;
		} catch (MyException e) {
			log.error("", e);
			refundService.refundSuccess(refund, e.getMessage());
		} catch (Exception e) {
			// 如果返回时发生异常，这时可能退款是成功的，但是这边显示退款失败会导致重复退款。所以出现异常一律先按成功处理。
			log.error("", e);
			refundService.refundSuccess(refund, "系统错误，按成功处理");
		}

		return refund;
	}

	// 退款
	public RefundResult queryRefund(String url, String mchid, String mchkey, String refundNo) {
		/** 公共请求map */
		Map<String, Object> requestMap = new HashMap<>();
		// 报文格式
		requestMap.put("format", "json");
		// 时间搓
		requestMap.put("timestamp", System.currentTimeMillis() + "");
		// 随机字符串
		requestMap.put("noncestr", RandomStringUtils.randomAlphanumeric(12));
		// 子商户号
		requestMap.put("mchid", mchid);
		// 命令 order
		requestMap.put("order", "refund");

		/** 实际请求内容 */
		JSONObject content = new JSONObject();
		content.put("out_refund_no", refundNo);
		// 报文内容
		requestMap.put("content", content.toJSONString());
		String respJson = post(url, requestMap, mchkey);
		if (respJson == null) {
			respJson = post(url, requestMap, mchkey);
		}
		RefundResult result = JSON.parseObject(respJson, RefundResult.class);
		return result;
	}
}
